import java.util.ArrayList;
import java.util.Collections;

public abstract class CollisionHelper
{
	private static final double ERROR_ADJUST = .001;
	
	public static CollisionData checkCollision(Prism p1, Prism p2, double tryMoveX, double tryMoveY, double tryMoveZ)
	{
		boolean touchingBottom = false;
		double dx = 0, dy = 0, dz = 0;
		if(tryMoveX > 0) dx = p1.getX() - p2.getX() - p2.getSizeX();
		else dx = p1.getX() + p1.getSizeX() - p2.getX();
		if(tryMoveY > 0) dy = p1.getY() - p2.getY() - p2.getSizeY();
		else dy = p1.getY() + p1.getSizeY() - p2.getY();
		if(tryMoveZ > 0) dz = p1.getZ() - p2.getZ() - p2.getSizeZ();
		else dz = p1.getZ() + p1.getSizeZ() - p2.getZ();
		
		if(Math.abs(tryMoveX) >= Math.abs(dx) || Math.abs(tryMoveY) >= Math.abs(dy) || Math.abs(tryMoveZ) >= Math.abs(dz)) //Optimization code
		{			
			double timeX = dx / tryMoveX, timeY = dy / tryMoveY, timeZ = dz / tryMoveZ;
			
			boolean didX = false, didY = false, didZ = false;
			boolean done = false;
			ArrayList<Double> listOfTimes = new ArrayList<Double>();
			listOfTimes.add(timeX);
			listOfTimes.add(timeY);
			listOfTimes.add(timeZ);
			Collections.sort(listOfTimes);
			for(int i = 0; i < 3 && !done; i++)
			{
				if(listOfTimes.get(i) == timeX && !didX && timeX >= 0 && timeX <= 1)
				{
					didX = true;
					double dPosY = timeX * tryMoveY;
					double dPosZ = timeX * tryMoveZ;
					if(p1.getY() < p2.getY() + p2.getSizeY() + dPosY && p2.getY() + dPosY < p1.getY() + p1.getSizeY()
							&& p1.getZ() < p2.getZ() + p2.getSizeZ() + dPosZ && p2.getZ() + dPosZ < p1.getZ() + p1.getSizeZ())
					{
						tryMoveX = dx;
						if(dx > 0) tryMoveX -= ERROR_ADJUST;
						else tryMoveX += ERROR_ADJUST;
					}
				}
				else if(listOfTimes.get(i) == timeY && !didY && timeY >= 0 && timeY <= 1)
				{
					done = true; //This must be done so that there are no weird places where you stop moving
					didY = true;
					double dPosX = timeY * tryMoveX;
					double dPosZ = timeY * tryMoveZ;
					if(p1.getX() < p2.getX() + p2.getSizeX() + dPosX && p2.getX() + dPosX < p1.getX() + p1.getSizeX()
							&& p1.getZ() < p2.getZ() + p2.getSizeZ() + dPosZ && p2.getZ() + dPosZ < p1.getZ() + p1.getSizeZ())
					{
						tryMoveY = dy;
						if(dy > 0) tryMoveY -= ERROR_ADJUST;
						else
						{
							tryMoveY += ERROR_ADJUST;
							touchingBottom = true;
						}
					}
				}
				else if(listOfTimes.get(i) == timeZ && !didZ && timeZ >= 0 && timeZ <= 1)
				{
					didZ = true;
					double dPosX = timeZ * tryMoveX;
					double dPosY = timeZ * tryMoveY;
					if(p1.getX() < p2.getX() + p2.getSizeX() + dPosX && p2.getX() + dPosX < p1.getX() + p1.getSizeX()
							&& p1.getY() < p2.getY() + p2.getSizeY() + dPosY && p2.getY() + dPosY < p1.getY() + p1.getSizeY())
					{
						tryMoveZ = dz;
						if(dz > 0) tryMoveZ -= ERROR_ADJUST;
						else tryMoveZ += ERROR_ADJUST;
					}
				}
			}
		}
		return new CollisionData(new double[]{tryMoveX, tryMoveY, tryMoveZ}, touchingBottom);
	}
	
	public static double[] checkCollisionNoTouch(Prism p1, Prism p2, double tryMoveX, double tryMoveY, double tryMoveZ)
	{
		double dx = 0, dy = 0, dz = 0;
		if(tryMoveX > 0) dx = p1.getX() - p2.getX() - p2.getSizeX();
		else dx = p1.getX() + p1.getSizeX() - p2.getX();
		if(tryMoveY > 0) dy = p1.getY() - p2.getY() - p2.getSizeY();
		else dy = p1.getY() + p1.getSizeY() - p2.getY();
		if(tryMoveZ > 0) dz = p1.getZ() - p2.getZ() - p2.getSizeZ();
		else dz = p1.getZ() + p1.getSizeZ() - p2.getZ();
		
		double timeX = dx / tryMoveX, timeY = dy / tryMoveY, timeZ = dz / tryMoveZ;
		
		boolean didX = false, didY = false, didZ = false;
		boolean done = false;
		ArrayList<Double> listOfTimes = new ArrayList<Double>();
		listOfTimes.add(timeX);
		listOfTimes.add(timeY);
		listOfTimes.add(timeZ);
		Collections.sort(listOfTimes);
		for(int i = 0; i < 3 && !done; i++)
		{
			if(listOfTimes.get(i) == timeX && !didX)
			{
				didX = true;
				if(timeX >= 0 && timeX <= 1)
				{
					double dPosY = timeX * tryMoveY;
					double dPosZ = timeX * tryMoveZ;
					if(p1.getY() < p2.getY() + p2.getSizeY() + dPosY && p2.getY() + dPosY < p1.getY() + p1.getSizeY()
					   && p1.getZ() < p2.getZ() + p2.getSizeZ() + dPosZ && p2.getZ() + dPosZ < p1.getZ() + p1.getSizeZ())
					{
						tryMoveX = dx;
						if(dx > 0) tryMoveX -= ERROR_ADJUST;
						else tryMoveX += ERROR_ADJUST;
					}
				}
			}
			else if(listOfTimes.get(i) == timeY && !didY)
			{
				didY = true;
				if(timeY >= 0 && timeY <= 1)
				{
					double dPosX = timeY * tryMoveX;
					double dPosZ = timeY * tryMoveZ;
					if(p1.getX() < p2.getX() + p2.getSizeX() + dPosX && p2.getX() + dPosX < p1.getX() + p1.getSizeX()
					   && p1.getZ() < p2.getZ() + p2.getSizeZ() + dPosZ && p2.getZ() + dPosZ < p1.getZ() + p1.getSizeZ())
					{
						tryMoveY = dy;
						if(dy > 0) tryMoveY -= ERROR_ADJUST;
						else tryMoveY += ERROR_ADJUST;
					}
				}
			}
			else if(listOfTimes.get(i) == timeZ && !didZ)
			{
				didZ = true;
				if(timeZ >= 0 && timeZ <= 1)
				{
					double dPosX = timeZ * tryMoveX;
					double dPosY = timeZ * tryMoveY;
					if(p1.getX() < p2.getX() + p2.getSizeX() + dPosX && p2.getX() + dPosX < p1.getX() + p1.getSizeX()
					   && p1.getY() < p2.getY() + p2.getSizeY() + dPosY && p2.getY() + dPosY < p1.getY() + p1.getSizeY())
					{
						tryMoveZ = dz;
						if(dz > 0) tryMoveZ -= ERROR_ADJUST;
						else tryMoveZ += ERROR_ADJUST;
					}
				}
			}
		}

		double[] tryCollision = {tryMoveX, tryMoveY, tryMoveZ};
		return tryCollision;
	}

}